import { css2dLabel } from '../basic';
import { LabelEnum } from '../types/command/types';
import { ManagerId } from 'nextcae-base';
import { useRenderStore } from '../store';
import { EntityManager, THREE } from '@amcax/web-render';
import { CS2DInputLabel } from '../dimension/label/label';
import { ParameterObjectType } from '@amcax/base';
import { DimCallback } from '../dimension/type';

export class ExtraLabel {
  private static instance: ExtraLabel;
  private css2DLabel: { [key in LabelEnum]?: css2dLabel | CS2DInputLabel } = {}; // 标签
  public extraCss2DLabel: css2dLabel[] = []; // 额外标签
  private assisMgr: EntityManager;
  static getInstance(): ExtraLabel {
    if (!this.instance) {
      this.instance = new ExtraLabel();
    }
    return this.instance;
  }
  /**
   * 设置 CSS2D 标签的相关信息。如果指定键的标签不存在，则创建新标签；如果存在，则更新其信息。
   * @param {string | number | HTMLDivElement} text - 标签显示的文本内容，可以是字符串、数字或 HTML 元素。默认值为空字符串。
   * @param {THREE.Vector3} position - 标签在三维空间中的位置。
   * @param {string} name - 标签的名称，默认值为 '_label-measure-info'。
   * @param {LabelEnum} key - 标签的枚举键，默认值为 LabelEnum.label。
   */
  public setCss2dLabel(
    text: string | number | HTMLDivElement = '',
    position: THREE.Vector3,
    name = '_label-measure-info',
    key = LabelEnum.label,
  ) {
    if (!this.css2DLabel?.[key]) {
      this.assisMgr = this.getMainRenderer().assisMgr;
      this.css2DLabel[key] = this.createLabel(text, position, name);
      this.css2DLabel[key].addTo(this.assisMgr.group);
    } else {
      (this.css2DLabel[key] as css2dLabel).update({
        text,
        pos: position,
      });
      this.changeCss2dLabel(true, key);
    }
  }
  getCSS2DLabel(key: LabelEnum = LabelEnum.label) {
    return this.css2DLabel[key];
  }
  inputLabelFocus(key: LabelEnum = LabelEnum.inputLabel) {
    if (this.css2DLabel[key] instanceof CS2DInputLabel) {
      this.css2DLabel[key]?.focus();
    }
  }
  inputLabelBur(key: LabelEnum = LabelEnum.inputLabel) {
    if (this.css2DLabel[key] instanceof CS2DInputLabel) {
      this.css2DLabel[key]?.blur();
    }
  }
  /**
   * 根据提供的文本、位置和名称创建并返回一个 CSS2D 标签实例。
   * @param {string | number | HTMLDivElement} text - 标签显示的文本内容，可以是字符串、数字或 HTML 元素。
   * @param {THREE.Vector3} position - 标签在三维空间中的位置。
   * @param {string} name - 标签的名称，默认值为 '_label-measure-info'。
   * @returns {css2dLabel} 新创建的 CSS2D 标签实例。
   */
  public createLabel(
    text: string | number | HTMLDivElement,
    position: THREE.Vector3,
    name = '_label-measure-info',
  ) {
    return new css2dLabel({ text, position, name });
  }
  public createUpdateInputLabel(
    text: number,
    position: THREE.Vector3,
    enterKey?: DimCallback,
    key = LabelEnum.inputLabel,
    prefix?: string,
    inputType = ParameterObjectType.Length,
  ) {
    if (!this.css2DLabel?.[key]) {
      this.assisMgr = this.getMainRenderer().assisMgr;
      this.css2DLabel[key] = this.createInputLabel(
        text,
        position,
        enterKey,
        inputType,
        prefix,
      );
      this.css2DLabel[key].clickFocus();
      this.css2DLabel[key].addTo(this.assisMgr.group);
    } else {
      (this.css2DLabel[key] as CS2DInputLabel).update({
        text,
        position,
      });
      this.changeCss2dLabel(true, key);
    }
  }
  public createInputLabel(
    text: number,
    position: THREE.Vector3,
    enterKey?: DimCallback,
    inputType = ParameterObjectType.Length,
    prefix?: string,
  ) {
    return new CS2DInputLabel({
      position,
      text,
      callback: enterKey,
      inputType: inputType,
      prefix,
    });
  }
  /**
   * 更改指定枚举键对应的 CSS2D 标签的显示状态。
   * 如果未提供具体的键，则会遍历所有标签枚举键，更改所有标签的显示状态。
   * @param {boolean} value - 一个布尔值，true 表示显示标签，false 表示隐藏标签。
   * @param {LabelEnum} key - 标签的枚举键，默认值为 LabelEnum.label。
   */
  public changeCss2dLabel(value: boolean, key = LabelEnum.label) {
    if (!key) {
      for (const key in LabelEnum) {
        this.css2DLabel[key]?.[value ? 'show' : 'hide']();
      }
    } else {
      this.css2DLabel[key]?.[value ? 'show' : 'hide']();
    }
  }
  /**
   * 设置指定枚举键对应的 CSS2D 标签的偏移量。
   * @param {{ x: number; y: number }} offset - 标签的偏移量，包含 x 和 y 两个属性。
   */
  public setCss2dOffset = async (offset?: { x: number; y: number }) => {
    if (
      !this.css2DLabel[LabelEnum.label] ||
      this.css2DLabel[LabelEnum.label] instanceof CS2DInputLabel
    ) {
      return;
    }
    const divElement = this.getDivElement();
    divElement.style.visibility = 'hidden';
    const res = await this.css2DLabel[LabelEnum.label].getWidthAndHeight();
    divElement.style.marginTop = (offset?.y ?? -(res.height / 2 + 8)) + 'px';
    divElement.style.marginLeft = (offset?.x ?? res.width / 2 + 8) + 'px';
    divElement.style.visibility = 'visible';
  };

  /**
   * 获取指定枚举键对应的 CSS2D 标签的 HTML 元素。
   * @returns {HTMLDivElement} 指定枚举键对应的 CSS2D 标签的 HTML 元素。
   * */
  private getDivElement = () => {
    if (
      !this.css2DLabel[LabelEnum.label] ||
      this.css2DLabel[LabelEnum.label] instanceof CS2DInputLabel
    ) {
      return null;
    }
    return this.css2DLabel[LabelEnum.label].labelObject.element;
  };
  /**
   * 移除所有额外的 CSS2D 标签。
   * 该方法会隐藏所有额外标签，并清空存储额外标签的数组。
   */
  public removeExtraCss2DLabel() {
    this.extraCss2DLabel.forEach((item) => {
      item?.destroy();
      item?.hide();
    });
    this.extraCss2DLabel = [];
  }
  /**
   * 移除所有 CSS2D 标签。
   * 该方法会隐藏所有标签，并清空存储标签的对象。
   * @returns {void} 无返回值。
   * */
  public removeCss2DLabel = (removeKey?: LabelEnum) => {
    if (!removeKey) {
      for (const key in LabelEnum) {
        this.css2DLabel[key]?.destroy();
        this.css2DLabel[key] = null;
      }
      this.removeExtraCss2DLabel();
    } else {
      this.css2DLabel[removeKey]?.destroy();
      this.css2DLabel[removeKey] = null;
    }
  };
  /**
   * 获取主渲染器相关信息。
   * 该方法从状态管理中获取主渲染器实例，并根据传入的管理器 ID 获取对应的实体管理器。
   * @param {ManagerId} managerId - 管理器的 ID，默认为 ManagerId.Assis。
   * @returns {{ mainRenderer: any, camera: any, assisMgr: EntityManager }} - 包含主渲染器、相机和实体管理器的对象。
   */
  private getMainRenderer(managerId: ManagerId = ManagerId.Assis) {
    const mainRenderer = useRenderStore.getState().mainRenderer;
    return {
      mainRenderer,
      camera: mainRenderer.cameraManager.camera,
      assisMgr: mainRenderer.getEntityManager(managerId),
    };
  }
  /**
   * 向额外 CSS2D 标签列表中添加一个新的 CSS2D 标签，并将其添加到场景中。
   * @param {css2dLabel} label - 要添加的 CSS2D 标签实例。
   */
  public addExtraCss2DLabel(label: css2dLabel) {
    this.extraCss2DLabel.push(label);
    label.addTo(this.assisMgr.group);
  }
}
export const labelManager = () => {
  return ExtraLabel.getInstance();
};
